package com.example.design_pattern.singleton;

//静态内部类实现单例（线程安全、效率高）

//这种方式下 Singleton 类被装载了，instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用，只有通过显式调用 getInstance 方法时，才会显式装载 SingletonHolder 类，从而实例化 instance。
//注意内部类SingletonHolder要用static修饰且其中的静态变量INSTANCE必须是final的。

// 个人备注：
// 这种写法的巧妙之处在与:它在懒汉的基础上改造。而我们说懒汉模式挺简单的，但是就是效率有点差，不管有没有用到，都new出实例。
// 而此种方式则把原先懒汉初始化实例的办法套在了一个内部内里面包裹了一下。顿时效果不一样了。因为类只有在被使用时才会被加载。所以，只有实际有人调用getInstance的时候，才会初始化出实例。

// 为什么instance变量，必须要用final修饰
// 静态内部类实现单例时，将 `instance` 成员变量声明为 `final` 是为了确保在多线程环境下对其进行安全访问。
// `final` 关键字可以保证变量的一次性初始化，一旦初始化完成就不会再被修改。这样可以确保在获取单例实例时，其初始化操作是线程安全的。

// 个人备注：
// 那么，通过内部类方式包裹初始化实例的代码，实现了按需创建，解决了性能不足的问题。
// 通过final修饰，解决了线程安全问题。(为什么Singleton01不用final呢？因为类加载只会加载一次，所以在自己类里面初始化，天然就是单线程的。但是写在内部类里面，为什么就要是final的???可以自己实际试验下，去掉finnal，是否会产生线程安全问题)

// 静态内部类实现单例，为什么instance成员变量，要用final修饰
// 静态内部类实现单例时，将 instance 成员变量声明为 final 是为了确保在多线程环境下对其进行安全访问。final 关键字可以保证变量的一次性初始化，一旦初始化完成就不会再被修改。这样可以确保在获取单例实例时，其初始化操作是线程安全的。

// 那为什么在饿汉单例模式下，instance却不需要finnal修饰，却可以保证线程安全呢？
// 在饿汉单例模式中，instance 在类加载的时候就被初始化了，因此不存在多线程竞争的情况，所以不需要使用 final 关键字来保证线程安全。由于在类加载过程中只会初始化一次 instance，所以不会存在线程安全问题。

// 那静态内部类实现单例模式下，内部类也仅仅只会加载一次啊，然后内部类被加载的时候，将instance化，后续内部类也不会第二次被加载了，那为什么内部类里面的instance还要用final修饰
// 在静态内部类实现单例模式中，将 instance 字段声明为 final 的主要目的是为了确保线程安全。尽管静态内部类在类加载时只会加载一次，保证了单例的实现，但如果不将 instance 字段声明为 final，在某些情况下，可能会发生指令重排序的问题，导致其他线程在访问到未完成初始化的 instance 对象。通过将 instance 字段声明为 final，可以避免这种情况的发生，从而确保线程安全。

// 为什么声明为final，就不会有"可能会发生指令重排序的问题，导致其他线程在访问到未完成初始化的 instance 对象"这种问题呢？
// 在 Java 中，对于被声明为 final 的字段，Java 内存模型（Java Memory Model，JMM）会对其进行特殊的处理。具体来说，当一个线程第一次访问一个包含 final 字段的对象时，JMM 会确保该对象的构造方法完成初始化操作之后，才会允许其他线程看到该对象的引用。这意味着，对于 final 字段，其初始化操作与对对象的引用发布操作是同步的，其他线程不会看到一个未完成初始化的 instance 对象。
//因此，将 instance 字段声明为 final 可以防止其他线程在对象初始化完成之前访问到它，从而避免了可能的指令重排序问题。这种保证对于单例模式的正确性至关重要。

// 难道对于非final类型的变量，jmm模型就可能会允许，在多线程情景下，一个变量没有初始化，就被其他另外一个线程访问到吗？
// 是的，对于非 final 类型的变量，Java 内存模型（JMM）并不会强制要求在对象初始化完成之前禁止其他线程访问到它。这就意味着，在某些情况下，可能会发生指令重排序的问题，导致其他线程在访问到未完成初始化的对象时发生错误。
//当一个线程访问一个包含非 final 字段的对象时，JMM 并不会对对象的初始化操作进行同步，因此在某些情况下，其他线程可能会看到对象的引用，但实际上对象的初始化尚未完成。这就可能导致其他线程在访问到未完成初始化的对象时出现错误。
//因此，在多线程情况下，为了确保对象的安全发布，推荐将对象引用声明为 final 类型，这样可以避免上述问题。

// 关于jmm的关于final变量的这项设计，我在浏览器如果想要查询更多信息，一般需要输入什么关键字？这项设计有自己专门的名字吗？为什么要这样设计？
// 要查询更多关于 Java 内存模型（JMM）中关于 `final` 变量的设计原理，你可以输入以下关键字进行搜索：
//1. Java Memory Model (JMM)
//2. Final variable semantics in Java
//3. Happens-before relationship in Java
//这项设计在 Java 中被称为 "final域半初始化" 或者 "final域重排序规则"。它的设计目的是确保在多线程环境下，使用 `final` 修饰的变量在构造函数完成之前不会被其他线程看到，从而保证了对象的安全发布。
//这样设计的原因是为了在多线程环境下保证程序的正确性和可靠性。由于 `final` 变量的一次赋值性质，它可以确保在构造函数完成之前不会被其他线程访问到，避免了对象未初始化完成就被其他线程使用的情况，从而提高了多线程环境下程序的安全性。
public class Singleton06 {

    // 内部类
    private static class SingletonHolder {
        private static final Singleton06 INSTANCE = new Singleton06();
    }

    // 构造函数
    private Singleton06 (){}

    public static final Singleton06 getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
